package com.liming.stock.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liming.auth.client.AdminAuthClient;
import com.liming.auth.client.UserAuthClient;
import com.liming.common.enums.DateScopeEnum;
import com.liming.common.enums.StockFlowEnum;
import com.liming.common.exception.BusinessException;
import com.liming.common.pojo.dto.stock.AddStockFlowDTO;
import com.liming.common.pojo.dto.stock.StockFlowPageQuery;
import com.liming.common.pojo.dto.stock.UserOrderProductListDTO;
import com.liming.common.pojo.dto.stock.UserOrderStockFlowDTO;
import com.liming.common.pojo.entity.BaseEntity;
import com.liming.common.pojo.entity.product.Product;
import com.liming.common.pojo.entity.stock.StockFlow;
import com.liming.common.pojo.vo.PageVO;
import com.liming.common.pojo.vo.order.OrderVO;
import com.liming.common.pojo.vo.stock.DailyFlowData;
import com.liming.common.pojo.vo.stock.ProductSalesCountVO;
import com.liming.common.pojo.vo.stock.StockDashboardVO;
import com.liming.common.pojo.vo.stock.StockFlowVO;
import com.liming.common.util.PageUtils;
import com.liming.common.util.TimeUtils;
import com.liming.common.util.mybatisplus.MPUtil;
import com.liming.order.client.UserOrderClient;
import com.liming.product.client.ProductClient;
import com.liming.stock.mapper.StockFlowMircoMapper;
import com.liming.stock.service.StockFlowMircoService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author liming
 * @date 2025/1/21
 */
@Slf4j
@Service
@AllArgsConstructor
public class StockFlowMircoServiceImpl extends ServiceImpl<StockFlowMircoMapper, StockFlow>
        implements StockFlowMircoService, MPUtil<StockFlow> {

    private final AdminAuthClient adminAuthClient;
    private final ProductClient productClient;
    private final UserOrderClient userOrderClient;
    private final UserAuthClient userAuthClient;

    @Override
    @Transactional
    public Boolean adminStockFlow(AddStockFlowDTO dto) {
        // 类型 退货
        // 退货 - 用户完成支付后提交退款申请 -> 管理员操作
        if (ObjectUtil.equal(dto.getType(), StockFlowEnum.RETURN.getCode())) {
            returnStock(dto);
            return true;
        }
        // 类型 入库
        // 类型 库存锁定
        // 类型 出库
        // 类型 库存解锁
        StockFlow stockFlow = new StockFlow();
        // 管理员操作
        preAdminSaveFLow(dto, stockFlow);
        Assert.isTrue(save(stockFlow), () -> new BusinessException("新增库存流水失败"));
        return true;
    }

    @Override
    public PageVO<StockFlowVO> pageFlow(StockFlowPageQuery query) {
        // 根据商品名称查询商品IDS
        Map<Long, Product> idProductMap = productClient.productIdsByLikeName(query.getProductName() == null ? "" : query.getProductName());
        if (CollUtil.isEmpty(idProductMap)) {
            return PageUtils.emptyPage();
        }
        // 查询所有库存流水记录
        IPage<StockFlow> rawPage = lambdaQuery()
                .in(CollUtil.isNotEmpty(idProductMap.keySet()), StockFlow::getProductId, idProductMap.keySet())
                .eq(ObjectUtil.isNotNull(query.getType()), StockFlow::getType, query.getType())
                .like(StrUtil.isNotBlank(query.getOperator()), StockFlow::getOperator, query.getOperator())
                .ge(ObjectUtil.isNotNull(query.getStartTime()), StockFlow::getCreateTime, query.getStartTime())
                .orderByDesc(BaseEntity::getCreateTime)
                .le(ObjectUtil.isNotNull(query.getEndTime()), StockFlow::getCreateTime, query.getEndTime())
                .page(setPage(query));
        return PageUtils.convert(rawPage, stockFlow -> {
            StockFlowVO stockFlowVO = new StockFlowVO();
            BeanUtils.copyProperties(stockFlow, stockFlowVO);
            Product product = idProductMap.getOrDefault(stockFlow.getProductId(), new Product());
            stockFlowVO.setProductName(product.getName());
            stockFlowVO.setModelNumber(product.getModelNumber());
            stockFlowVO.setProductImage(product.getMainImageUrl());
            if (ObjectUtil.isNotNull(stockFlow.getOrderId())) {
                // 查询订单信息
                OrderVO orderVO = userOrderClient.detailById(stockFlow.getOrderId());
                stockFlowVO.setOrderNo(orderVO.getOrderNo());
            }
            return stockFlowVO;
        });
    }

    private void returnStock(AddStockFlowDTO dto) {
        Assert.notNull(dto.getOrderId(), () -> new BusinessException("退货 - 订单ID不能为空"));
        // 管理员操作
        StockFlow returnStock = new StockFlow();
        String remark = dto.getRemark();
        preAdminSaveFLow(dto, returnStock);
        // 存在订单ID
        // 用户下单 用户取消订单 - 库存出库 -> 库存退货
        List<StockFlow> outFlowList = lambdaQuery()
                .eq(StockFlow::getOrderId, dto.getOrderId())
                .eq(StockFlow::getType, StockFlowEnum.OUT.getCode()).list();
        Assert.notEmpty(outFlowList, () -> new BusinessException("退货 - 出库库存流水不存在"));
        for (StockFlow stockFlow : outFlowList) {
            stockFlow.setType(StockFlowEnum.RETURN.getCode());
            stockFlow.setRemark(remark);
        }
        Assert.isTrue(saveBatch(outFlowList), () -> new BusinessException("退货 - 新增库存流水失败"));
    }

    private void preAdminSaveFLow(AddStockFlowDTO dto, StockFlow flow) {
        String operator = adminAuthClient.getNameAndPhone(Long.parseLong(dto.getOperatorId()));
        BeanUtils.copyProperties(dto, flow);
        flow.setOperator(operator);
        flow.setRemark(dto.getRemark());
    }

    @Override
    public List<StockFlow> listLockFlowByOrderId(Long orderId) {
        List<StockFlow> lockStockFlowList = lambdaQuery()
                .eq(StockFlow::getOrderId, orderId)
                .eq(StockFlow::getType, StockFlowEnum.LOCK.getCode()).list();
        Assert.notEmpty(lockStockFlowList, () -> new BusinessException("锁定库存流水不存在"));
        return lockStockFlowList;
    }

    @Override
    @Transactional
    public Boolean userOrderLockStockFlow(UserOrderStockFlowDTO userOrderStockFlowDTO) {
        String userId = userOrderStockFlowDTO.getUserId();
        Map<Long, String> userIdAndNicknameMap = userAuthClient.userIdsAndNickname();
        // 库存锁定 - 用户下单 - 未支付
        List<StockFlow> lockFLowList = new ArrayList<>();
        List<UserOrderProductListDTO> productList = userOrderStockFlowDTO.getProductList();
        for (UserOrderProductListDTO product : productList) {
            StockFlow lockFlow = new StockFlow();
            lockFlow.setOrderId(userOrderStockFlowDTO.getOrderId());
            lockFlow.setStockId(product.getStockId());
            lockFlow.setProductId(product.getProductId());
            lockFlow.setType(StockFlowEnum.LOCK.getCode());
            lockFlow.setQuantity(product.getQuantity());
            lockFlow.setRemark("用户下单 - 锁定库存");
            lockFlow.setOperator(userIdAndNicknameMap.getOrDefault(Long.parseLong(userId), ""));
            lockFLowList.add(lockFlow);
        }
        Assert.isTrue(saveBatch(lockFLowList), () -> new BusinessException("用户下单（未支付） - 锁定库存 - 新增库存流水失败"));
        return true;
    }

    @Override
    @Transactional
    public Boolean userOrderOutStockFLow(Long orderId) {
        // 库存出库 - 用户下单 - 已支付
        List<StockFlow> stockFlowList = this.listLockFlowByOrderId(orderId);
        for (StockFlow stockFlow : stockFlowList) {
            stockFlow.setId(null);
            stockFlow.setCreateTime(null);
            stockFlow.setType(StockFlowEnum.OUT.getCode());
            stockFlow.setRemark("用户完成支付 - 出库");
        }
        Assert.isTrue(saveBatch(stockFlowList), () -> new BusinessException("用户下单（完成支付） - 出库 - 新增库存流水失败"));
        return true;
    }

    @Override
    @Transactional
    public Boolean userOrderUnlockStockFLow(Long orderId) {
        // 库存解锁 - 用户下单（未支付） - 取消订单
        List<StockFlow> stockFlowList = this.listLockFlowByOrderId(orderId);
        for (StockFlow stockFlow : stockFlowList) {
            stockFlow.setId(null);
            stockFlow.setCreateTime(null);
            stockFlow.setType(StockFlowEnum.UNLOCK.getCode());
            stockFlow.setRemark("用户取消支付 - 库存解锁");
        }
        Assert.isTrue(saveBatch(stockFlowList), () -> new BusinessException("用户下单（取消订单） - 出库 - 新增库存流水失败"));
        return true;
    }

    @Override
    public StockDashboardVO flowDashboard(String scope) {
        TimeUtils.LocalDateTimeRange range = TimeUtils.getByDateScopeEnum(DateScopeEnum.getByScope(scope));
        LocalDateTime start = range.getStart();
        LocalDateTime end = range.getEnd();
        // 查询区间内的库存
        List<StockFlow> flowList = this.lambdaQuery().ge(BaseEntity::getCreateTime, start).le(BaseEntity::getCreateTime, end).list();
        Map<LocalDate, Map<Integer, Long>> dailyFlowMap = flowList.stream()
                .collect(
                        Collectors.groupingBy(stockFlow -> stockFlow.getCreateTime().toLocalDate(),
                                Collectors.groupingBy(StockFlow::getType, Collectors.summingLong(StockFlow::getQuantity)))
                );
        // 将数据转换为数组
        List<DailyFlowData> dailyStockDataList = new ArrayList<>();
        for (LocalDate date = start.toLocalDate(); !date.isAfter(end.toLocalDate()); date = date.plusDays(1)) {
            Map<Integer, Long> typeCountMap = dailyFlowMap.getOrDefault(date, Collections.emptyMap());
            dailyStockDataList.add(new DailyFlowData(date, typeCountMap));
        }
        // 创建 StockDashboardVO 并返回
        StockDashboardVO stockDashboardVO = new StockDashboardVO();
        stockDashboardVO.setDailyFlowData(dailyStockDataList);
        return stockDashboardVO;
    }


    @Override
    public List<ProductSalesCountVO> salesCountByTime(DateScopeEnum dateScopeEnum) {
        TimeUtils.LocalDateTimeRange range = TimeUtils.getByDateScopeEnum(dateScopeEnum);
        LocalDateTime start = range.getStart();
        LocalDateTime end = range.getEnd();
        List<StockFlow> flowList = lambdaQuery()
                .ge(BaseEntity::getCreateTime, start)
                .le(BaseEntity::getCreateTime, end)
                .eq(StockFlow::getType, StockFlowEnum.OUT.getCode())
                .list();
        // 根据商品ID进行分组，并统计每个商品的销售数量
        Map<Long, Integer> groupByProductId = flowList.stream()
                .collect(Collectors.groupingBy(StockFlow::getProductId, Collectors.summingInt(StockFlow::getQuantity)));
        return groupByProductId.entrySet().stream().map(entry -> {
            ProductSalesCountVO vo = new ProductSalesCountVO();
            vo.setProductId(entry.getKey());
            vo.setSalesCount(entry.getValue());
            return vo;
        }).toList();
    }

    @Override
    public List<Long> hotSalesProductIds() {
        // 获取本月时间
        TimeUtils.LocalDateTimeRange range = TimeUtils.getThisMonthDateRange();
        List<StockFlow> flowList = lambdaQuery()
                .eq(StockFlow::getType, StockFlowEnum.OUT.getCode())
                .ge(BaseEntity::getCreateTime, range.getStart())
                .le(BaseEntity::getCreateTime, range.getEnd())
                .list();
        Map<Long, Integer> productIdAndQuantity = flowList.stream().collect(Collectors.groupingBy(StockFlow::getProductId, Collectors.summingInt(StockFlow::getQuantity)));
        return productIdAndQuantity.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).limit(10).map(Map.Entry::getKey).toList();
    }

    @Override
    public List<StockFlow> listOutFlowByOrderId(Long orderId) {
        List<StockFlow> flowList = lambdaQuery()
                .eq(StockFlow::getOrderId, orderId)
                .eq(StockFlow::getType, StockFlowEnum.OUT.getCode())
                .list();
        Assert.notEmpty(flowList, () -> new BusinessException("出库库存流水不存在"));
        return flowList;
    }

    @Override
    @Transactional
    public List<StockFlow> addRefundFlowBecauseRefundSuccess(Long orderId) {
        List<StockFlow> result = listOutFlowByOrderId(orderId);

        List<StockFlow> copyFlowList = result.stream().toList();
        for (StockFlow refundFlow : copyFlowList) {
            refundFlow.setId(null);
            refundFlow.setCreateTime(null);
            refundFlow.setType(StockFlowEnum.RETURN.getCode());
            refundFlow.setRemark("用户退款成功 - 退款");
        }
        Assert.isTrue(saveBatch(copyFlowList), () -> new BusinessException("用户下单（取消订单） - 出库 - 新增库存流水失败"));
        return result;
    }
}
